package com.wolfking.jeesite.sso;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.wolfking.jeesite.config.SsoConfig;
import com.wolfking.jeesite.entity.Menu;
import com.wolfking.jeesite.entity.User;
import com.wolfking.jeesite.exception.ExceptionCode;
import com.wolfking.jeesite.exception.JeesiteException;
import com.wolfking.jeesite.service.MenuService;
import com.wolfking.jeesite.service.UserService;
import com.wolfking.jeesite.util.CookieUtil;
import com.wolfking.jeesite.util.OsUtils;
import com.wolfking.jeesite.util.PasswordUtil;
import com.wolfking.jeesite.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户单点登录的service
 *
 * @author wolfking@赵伟伟
 * @mail zww199009@163.com
 * @创作日期 2017年4月23日 下午4:58:50
 * @copyright wolfking
 */
@Slf4j
@Service
public class DefaultSsoService implements SsoService {

    @Autowired
    private SsoCacheService ssoCacheService;

    @Autowired
    private UserService userService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private SsoConfig ssoConfig;

    @Autowired
    private SsoConfigService ssoConfigService;

    @Autowired(required = false)
    private List<LoginAdapter> loginAdapters;

    /**
     * 获取用户登录的tokenId
     *
     * @param request
     * @return
     */
    @Override
    public String getTokenId(HttpServletRequest request) {
        Object tokenId = request.getAttribute("tokenId");
        if (Objects.isNull(tokenId) || StringUtils.isBlank(tokenId.toString())) {
            return CookieUtil.getCookie(request, "tokenId");
        } else {
            log.info("getToken from request attribute");
            return tokenId.toString().trim();
        }
    }

    /**
     * 根据tokenid 获取user
     *
     * @param tokenId
     * @return
     */
    @Override
    public User getTokenUser(String tokenId) {
        try {
            return ssoCacheService.getTokenUser(tokenId);
        } catch (Exception e) {
            log.error("get token user error", e);
            return null;
        }
    }

    /**
     * 获取当前的用户
     *
     * @return
     */
    @Override
    public User getUser() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            User tokenUser = null;
            String tokenId = getTokenId(requestAttributes.getRequest());
            if (StringUtils.isNotBlank(tokenId)) {
                tokenUser = getTokenUser(tokenId);
            }
            //本地debug
            if (Objects.isNull(tokenUser)) {
                tokenUser = getDebugUser(requestAttributes);
            }
            return tokenUser;
        }
        return null;
    }

    private User getDebugUser(ServletRequestAttributes requestAttributes) {
        if (OsUtils.isWindows() && StringUtils.isNotBlank(ssoConfig.getDebugUserName()) && Objects.nonNull(requestAttributes)) {
            try {
                User tokenUser = new User();
                tokenUser.setLoginName(ssoConfig.getDebugUserName());
                List<User> list = userService.selectExample(tokenUser);
                if (CollectionUtils.isEmpty(list)) {
                    return null;
                }
                tokenUser = list.get(0);
                login(tokenUser);
                CookieUtil.setCookie(requestAttributes.getRequest(), requestAttributes.getResponse(), "tokenId", tokenUser.getTokenId());
                return tokenUser;
            } catch (Exception e) {
                return null;
            }
        }
        return null;
    }


    /**
     * 获取用户所有的鉴权码
     *
     * @param userId
     * @return
     */
    @Override
    public Set<String> getUserAuthCodes(Integer userId) {
        try {
            if (userId == 1) {
                return menuService.getUserMenus(1).stream().map(Menu::getPermission).filter(StringUtils::isNotBlank).collect(Collectors.toSet());
            } else {
                Set<String> set = Sets.newHashSet();
                List<String> list = userService.getUserAllAuthCodes(userId);
                if (list != null) {
                    set.addAll(list);
                }
                return set;
            }
        } catch (Exception e) {
            log.error("get getUserAuthCodes error", e);
            return Sets.newHashSet();
        }
    }

    @Override
    public Set<String> getUserAuthCodes() {
        return getUserAuthCodes(getUser().getId());
    }

    @Override
    public String logout(String tokenId) {
        return ssoCacheService.clearToken(tokenId);
    }

    @Override
    public User login(String username, String password) {
        User user = new User();
        user.setLoginName(username);
        List<User> list = userService.selectExample(user);
        if (CollectionUtils.isEmpty(list)) {
            if (!CollectionUtils.isEmpty(loginAdapters)) {
                for (LoginAdapter loginAdapter : loginAdapters) {
                    if (loginAdapter.login(username, password)) {
                        log.info("use adapter {},{} login success", loginAdapter.name(), username);
                        list = userService.selectExample(user);
                        user = list.get(0);
                        return login(user);
                    }
                }
            }
        }
        if (CollectionUtils.isEmpty(list)) {
            throw new JeesiteException(ExceptionCode.BIZ_ERROR_USER_NOT_EXIST);
        }
        user = list.get(0);
        if (!PasswordUtil.validatePassword(password, user.getPassword())) {
            if (!CollectionUtils.isEmpty(loginAdapters)) {
                for (LoginAdapter loginAdapter : loginAdapters) {
                    if (loginAdapter.login(username, password)) {
                        log.info("use adapter {},{} login success", loginAdapter.name(), username);
                        list = userService.selectExample(user);
                        user = list.get(0);
                        return login(user);
                    }
                }
            }
            throw new JeesiteException(ExceptionCode.BIZ_ERROR_USER_COUNT_NAME_NOMATHCH);
        }
        return login(user);
    }

    @Override
    public User login(User user) {
        if ("1".equals(user.getDelFlag())) {
            throw new JeesiteException(ExceptionCode.BIZ_ERROR_USER_NOT_AVAILABLE);
        }
        String tokenId = user.getTokenId();
        if (StringUtils.isBlank(tokenId)) {
            tokenId = UUID.randomUUID().toString().replace("-", "");
        }
        user.setTokenId(tokenId);
        ssoCacheService.saveToken(user);
        return user;
    }

    @Override
    public Map<String, Object> loginTrace() {
        Map<String, Object> map = Maps.newHashMap();
        map.put("login", getUser() != null);
        map.put("loginUrl", ssoConfig.getSelfRootUrlByRequest() + "/login");
        return map;
    }


    //sso登录获取用户信息
    @Override
    public User checkOtherUser(HttpServletRequest request) {
        String username = "";
        String ssoLoginInfo = ssoConfigService.getSsoLoginInfo();
        String ticketStr = "ticket";
        String ticket = request.getParameter(ticketStr);
        log.info("ticket is {}", ticket);
        if (StringUtils.isNotBlank(ssoLoginInfo) && StringUtils.isNotBlank(ticket)) {
            try {
                String url = ssoLoginInfo + "?ticket=" + ticket
                        + "&service=" + RequestUtil.getRequestUrl(request, Maps.newHashMap(), ticketStr) +
                        "&pgtUrl" + RequestUtil.getRequestUrl(request, ImmutableMap.of("proxyResponse", "true"), ticketStr);
                log.info("request sso ticket user url is {}", url);
                RestTemplate restTemplate = new RestTemplate();
                HttpHeaders headers = new HttpHeaders();
                HttpEntity<String> requestEntity = new HttpEntity<>(null, headers);
                String forObject = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class).getBody();
                String begin = "<cas:user>";
                String end = "</cas:user>";
                log.info("request sso response is {}", forObject);
                if (StringUtils.isNotBlank(forObject) && forObject.contains(begin) && forObject.contains(end)) {
                    log.info("request sso ticket user url response is {}", forObject);
                    username = forObject.substring(forObject.indexOf(begin) + begin.length(), forObject.indexOf(end));
                    log.info("username is {}", username);
                    QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
                    userQueryWrapper.eq("login_name", username);
                    User one = userService.getOne(userQueryWrapper);
                    if (Objects.isNull(one)) {
                        one = userService.getById(1);
                        one.setId(null);
                        one.setLoginName(username);
                        one.setName(username);
                        one.setPassword(PasswordUtil.entryptPassword(username));
                        one.setPhoto(null);
                        one.setVersion(0);
                        one.setCreateTime(new Date());
                        one.setUpdateTime(new Date());
                        one.setPhone(null);
                        one.setMobile(null);
                        one.setRemarks(username);
                        one.setLoginIp(request.getRemoteAddr());
                        one.setLoginDate(new Date());

                        String ssoRegisterRoles = ssoConfigService.getSsoRegisterRoles();
                        if (StringUtils.isNoneBlank(ssoRegisterRoles)) {
                            Set<Integer> roleIdSet = Arrays.stream(ssoRegisterRoles.split(","))
                                    .map(Integer::parseInt).collect(Collectors.toSet());
                            one.setRoleIdList(roleIdSet);
                        }
                        userService.save(one);
                    }
                    one.setTokenId(ticket);
                    return one;
                }
            } catch (Exception e) {
                log.warn("", e);
            }
        }
        return null;
    }
}
